local _ENV,_START = hf_setenv('card_king_rank',true)

local skynet = require "skynet"
local cjson = require "cjson"
local util = require "util"
local reason = require "reason"
local sharedata  = require "skynet.sharedata"
local server_config = dofile(skynet.getenv("server_config"))

local table_insert = table.insert
local table_remove = table.remove
local table_sort = table.sort
local math_random = math.random
local math_floor = math.floor
local string_format = string.format

local MAX_RANK = 20
local handler = {}

function init()
    global_configs = nil
    robots_list = {}
    rank_list = {}
    last_week_rank_list = {}

    rank_map = {}

    redis_pool = 0
    next_robot_update_time = 0

    activity_id = 0
    robot_uid = 0

    hf_set_sentinel_end(_ENV)
end

local function get_robot_uid()
    if robot_uid < -32767 then
        robot_uid = 0
    end

    robot_uid = robot_uid - 1
    return robot_uid
end

function make_activity_key()
    return string_format('activity_rank_%d',activity_id)
end

function get_vip_by_key(vip_key)
    local key_pre = 'vip_'
    local vip_str = vip_key:sub(key_pre:len()+1)
    return tonumber(vip_str)
end

function get_rank_robot_vip_border()
    local vip_rate_range = global_configs.robot_rank_vip
    local vip_conf = global_configs.vip
    if not vip_rate_range or not vip_conf then
        errlog('config rank_vip_conf.lua or vip_conf.lua error')
        return
    end
    
    local vip_level = 0
    local vip_border = ''
    local rand_rate = math_random(1,100)
    for level,rate in pairs(vip_rate_range) do 
        if rand_rate < rate then
            vip_level = get_vip_by_key(level)
            break
        end

        rand_rate = rand_rate - rate
    end

    if vip_level ~= 0 then
        for _,vip_info in pairs(vip_conf) do
            if vip_info.level == vip_level then
                vip_border = vip_info.icon_border_url
                break
            end
        end
    end

    return vip_level,vip_border
end

local function clear_rank()
    rank_list = {}
    for robot_id,info in pairs(robots_list) do
        info.curr_value = 0
    end

    local rank_key = make_activity_key()
    local conn = redis_pool:get_conn_from_pool()
    local cache_data = conn:del(rank_key)
    redis_pool:putback_to_pool(conn)
end

local function sort_rank(table_list,limit_num)
    table_sort(table_list,function(a,b)
        if a.value ~= b.value then
            return a.value > b.value
        end
        
        return a.uid < b.uid
    end)

    local ret_rank = table_list
    local tem_rank_map = {}
    if limit_num and limit_num > 0 then
        ret_rank = {}
        local i = 1
        while i <= #table_list and i <= limit_num do
            table_insert(ret_rank,table_list[i])
            tem_rank_map[table_list[i].uid] = i
            i = i + 1
        end
    end

    return ret_rank,tem_rank_map
end

local function save_rank()
    local save_data = {}
    save_data.robots_list = robots_list
    save_data.rank_list = rank_list
    save_data.last_week_rank_list = last_week_rank_list
    save_data.robot_uid = robot_uid

    local rank_key = make_activity_key()
    local conn = redis_pool:get_conn_from_pool()
    local rank_data = cjson.encode(save_data)
    local cache_data = conn:set(rank_key,rank_data)
    redis_pool:putback_to_pool(conn)
end

local function update_rank(uid,value)
    if not uid or not value then
        return 
    end
    
    local need_save = false
    local rank = rank_map[uid]
    if rank and rank <= #rank_list then
        rank_list[rank].value = value
        rank_list,rank_map = sort_rank(rank_list,MAX_RANK)
        need_save = true
    end

    if (#rank_list < MAX_RANK and not rank) or (not rank and value > rank_list[#rank_list].value) then
        table_insert(rank_list,{uid = uid,value = value})
        rank_list,rank_map = sort_rank(rank_list,MAX_RANK)
        need_save = true
    end

    if need_save then
        save_rank()
    end

    print(uid,"update_rank-->",value,need_save,type(uid))
end

local function update_exvalue(award_list)
    if not award_list then
        return 
    end

    for _,award_data in pairs(award_list) do
        local rank = rank_map[award_data.uid]
        if rank and rank <= #rank_list then
            rank_list[rank].exvalue = award_data.award_coins
        end
    end

    save_rank()
end

function get_rank(rank_info)

    local uid_list = {}
    for _,v in pairs(rank_info) do
        table_insert(uid_list,v.uid)
    end

    local ok,uid_info_map = R().friendsvr(1):call('.info_mgr','get_infos',uid_list)
    if not ok then
        errlog(uid,'failed to info_mgr')
        return skynet.retpack(false) 
    end

    local ret_rank_list = {}
    for index,v in pairs(rank_info) do
        if index > MAX_RANK then
            break
        end

        local uid = v.uid
        local info = uid_info_map[uid] or robots_list[uid] or {}
        
        local vip_level = info.vip_level
        if not robots_list[uid] then
            local ok,vlevel = R().basesvr{key=uid}:call('.msg_handler', 'get_vip_level',uid)
            if vlevel then
                vip_level = vlevel
            end
        end

        table_insert(ret_rank_list,{
            uid = uid,
            name  = info.name,
            icon  = info.icon,
            value = v.value,
            is_robot = info.is_robot or false,
            exvalue = v.exvalue,
            vip_level = vip_level or 0,
            icon_border = info.icon_border,
        })
    end

    print_r(ret_rank_list)
    return ret_rank_list
end

local function get_player_rank(uid,rank_list)
    local ret_rank = 0
    for rank,rank_data in pairs(rank_list) do
        if rank_data.uid == uid then
            ret_rank = rank
            break
        end
    end

    return ret_rank
end
--[[
    robot_name
    robot_icon
    robot_value
]]
function handler.add_robot(robot_name,robot_icon,robot_value)
    if not robot_name or not robot_icon or not robot_value then
        errlog("add_robot params err",robot_name,robot_icon,robot_value)
        skynet.retpack(false)
        return
    end

    local robot_id = get_robot_uid()
    local vip,bordor = get_rank_robot_vip_border()

    robots_list[robot_id] = {id = robot_id,max_value = robot_value,name = robot_name,icon = robot_icon,curr_value = 0,
        vip_level = vip,icon_border = bordor,is_robot = true}

    skynet.retpack(true)
end

--[[
    robot_id
    robot_value
]]
function handler.add_robot_value(robot_id,robot_value)
    if not robot_id or not robot_value or not robots_list[robot_id] then
        errlog(robot_id,"add_robot_value",robot_value)
        skynet.retpack(false)
        return
    end

    local robot_info = robots_list[robot_id]
    robot_info.max_value = robot_info.max_value + robot_value
    skynet.retpack(true)
end

function handler.update_rank(uid,value)
    update_rank(uid,value)
    skynet.retpack(true)
end
function handler.update_rank_exvalue(uid,award_list)
    update_rank_exvalue(uid,award_list)
end

function handler.close_rank()
    clear_rank()
    skynet.exit()
end

function handler.exit()
    skynet.exit()
end

function handler.reset_rank()
    last_week_rank_list = rank_list
    rank_list = {}
    rank_map = {}
    for robot_id,info in pairs(robots_list) do
        info.curr_value = 0
    end

    save_rank()
    skynet.retpack(true)
end

function handler.get_curr_rank()
    return skynet.retpack(get_rank(rank_list))
end

function handler.get_last_week_rank()
    return skynet.retpack(get_rank(last_week_rank_list))
end

function handler.get_player_rank(uid)
    local rank = get_player_rank(uid,rank_list)
    return skynet.retpack(rank)
end

function handler.update_rank_exvalue(award_list)
    update_exvalue(award_list)
end

function update_robot_value(curr_time)
    if next_robot_update_time < curr_time then
        local activity_conf = global_configs.activity[activity_id]
        local robot_refresh_score = activity_conf.ac_content.robot_refresh_score or {5,50}
        local robot_refresh_time = activity_conf.ac_content.robot_refresh_time or {30,30}
        for id,info in pairs(robots_list) do
            --涨5~50分（5的倍数）
            if info.curr_value < info.max_value then
                local rand_value = math_random(robot_refresh_score[1],robot_refresh_score[2])
                local add_value = math_floor(rand_value / 5) * 5
                if add_value > info.max_value - info.curr_value then
                    add_value = info.max_value - info.curr_value
                end
                
                info.curr_value = info.curr_value + add_value
                update_rank(info.id,info.curr_value)
            end
        end
        --下次刷新时间为1~5分钟
        next_robot_update_time = curr_time + math_random(robot_refresh_time[1],robot_refresh_time[2])
        print("@@@@@@@@next_robot_update_time",next_robot_update_time,curr_time)
        print_r(robots_list)
    end

    --print(next_robot_update_time,curr_time)
end

local function update_robot()
    while true do
        local curr_time = util.get_now_time()
        local ok,ret = xpcall(update_robot_value,debug.traceback,curr_time)
        if not ok then
            errlog(ret)
        end
        
        skynet.sleep(100)
    end
end

local function load_rank_data()
    local rank_key = make_activity_key()
    local conn = redis_pool:get_conn_from_pool()
    local cache_data = conn:get(rank_key)
    if cache_data then
        cache_data = cjson.decode(cache_data)
        robots_list = cache_data.robots_list
        rank_list = cache_data.rank_list
        last_week_rank_list = cache_data.last_week_rank_list
        robot_uid = tonumber(cache_data.robot_uid or 0)
        --
        local tem_robots_list = {}
        for id,info in pairs(robots_list) do
            tem_robots_list[tonumber(id)] = info
        end

        for id,info in pairs(tem_robots_list) do
            if not info.vip_level then
                local vip,bordor = get_rank_robot_vip_border()
                info.vip_level = vip
                info.icon_border = bordor
            end
        end

        robots_list = tem_robots_list
        rank_list,rank_map = sort_rank(rank_list,MAX_RANK)
        print_r(cache_data)
    end
    
    redis_pool:putback_to_pool(conn)
end

local function init_rank_list()
    local ok,ret = xpcall(load_rank_data,debug.traceback)
    if not ok then
        errlog('failed to load_rank_data',ret)
    end
end

local CMD = {}
--[[  conf
    redis_host
    redis_port
    redis_db
    redis_auth
    activity_id 活动id
]]
function CMD.start(conf)
    print_r(conf)
    local redis_conf = {
        host = assert(server_config.redis_conf.host),
        port = assert(server_config.redis_conf.port),
        db = assert(server_config.redis_conf.db),
        auth = server_config.redis_conf.auth,
    }

    redis_pool = require("redis_pool").new(redis_conf)
    activity_id = conf.activity_id or 0
    init_rank_list()
    skynet.fork(update_robot)
    skynet.dispatch('lua',function(session, source, cmd, ...)
        dbglog('activity_rank_handler---->',cmd,...)
        local f = assert(handler[cmd],'rank_handler---->unknown cmd ' .. cmd)
        f(...)
    end)

    skynet.retpack(true)
end

function start()
    skynet.start(function()
        skynet.dispatch("lua", function(session, source, cmd, ...)
        local f = CMD[cmd]
            if f then
                f(...)
            else
                errlog("unknown cmd ",cmd)
                skynet.retpack(false)
            end
        end)

        -- sharedata.query("global_configs")
        -- global_configs = setmetatable({},{
        --     __index = function(t,k)
        --         return sharedata.query("global_configs")[k]
        --     end
        -- })
        global_configs = setmetatable({},{
            __index = function(t,k) 
                --return sharedata.query("global_configs")[k]
                return sharedata.query(k)
            end
        })
        
    end)
end

_START(function()
    print('now this card_king_rank is starting')
    init()
    start()
end)